package net.oschina.arvin.sqlbuilder.definition;

import net.oschina.arvin.sqlbuilder.annotations.IgnoreField;
import net.oschina.arvin.sqlbuilder.annotations.Table;
import net.oschina.arvin.sqlbuilder.enums.ColumnMapStrategy;
import net.oschina.arvin.sqlbuilder.utils.AssertUtil;
import net.oschina.arvin.sqlbuilder.utils.ReflectUtil;
import net.oschina.arvin.sqlbuilder.utils.Util;

import java.lang.reflect.Field;
import java.util.*;

/**
 * 模型类定义
 *
 * @author Arvin
 * @time 2017/11/15 23:37
 */
public class ModelDefinition {

    /** 模型类型 */
    private final Class<?> modelType;

    /** 表 Table Annotation */
    private Table tableAnnotation;

    /** 数据库表名称 */
    private String wrapTablename;
    private String unWrapTablename;

    /** 表级别列映射策略 */
    private ColumnMapStrategy tableColumnMapStrategy;

    /** 主键定义列表 */
    private List<ColumnDefinition> primaryKeyDefinitionList = new ArrayList<ColumnDefinition>();

    /** 唯一键定义列表 */
    private List<ColumnDefinition> uniqueKeyDefinitionList = new ArrayList<ColumnDefinition>();

    /** 列定义列表 */
    private List<ColumnDefinition> columnDefinitionList = new ArrayList<ColumnDefinition>();

    /** 数据库列 --> 列定义 */
    private Map<String, ColumnDefinition> columnNameToColumnDefinitionMap = new HashMap<String, ColumnDefinition>();

    /** Java 属性名 --> 列定义 */
    private Map<String, ColumnDefinition> modelFieldNameToColumnDefinitionMap = new HashMap<String, ColumnDefinition>();

    /** Java 属性对象 --> 列定义 */
    private Map<Field, ColumnDefinition> modelFieldToColumnDefinitionMap = new HashMap<Field, ColumnDefinition>();

    public ModelDefinition(Class<?> modelType) {
        this.modelType = modelType;
        parseDefinition();
    }

    private static ColumnMapStrategy parseColumnMapStrategyByTableDefinition(Table tableDefinition, ColumnMapStrategy defaultColumnMapStrategy) {
        defaultColumnMapStrategy = null == defaultColumnMapStrategy ? ColumnMapStrategy.UNDERLINE : defaultColumnMapStrategy;
        if (null == tableDefinition) {
            return defaultColumnMapStrategy;
        }
        if (null != tableDefinition.mapStrategy()) {
            return tableDefinition.mapStrategy();
        }
        return defaultColumnMapStrategy;
    }

    /**
     * 解析 Model 定义
     */
    private void parseDefinition() {

        // 解析数据表相关属性
        {
            this.tableAnnotation = ReflectUtil.getClassAnnotation(this.modelType, Table.class);
            this.tableColumnMapStrategy = parseColumnMapStrategyByTableDefinition(this.tableAnnotation, ColumnMapStrategy.UNDERLINE);
            String tableName;
            if (null != this.tableAnnotation && Util.isNotBlank(this.tableAnnotation.name())) {
                tableName = this.tableAnnotation.name();
            } else {
                tableName = this.tableColumnMapStrategy.getMapColumn(this.modelType.getSimpleName());
            }
            this.unWrapTablename = Util.unWrapTableName(tableName);
            this.wrapTablename = Util.wrapTableName(tableName);
        }

        List<Field> fieldList = ReflectUtil.getAllNoneStaticDeclaredFields(this.modelType);

        if (!fieldList.isEmpty()) {
            for (Field modelField : fieldList) {

                if (isIgnoreField(modelField)) {
                    continue;
                }

                ColumnDefinition columnDefinition = new ColumnDefinition(modelField, tableColumnMapStrategy);

                this.columnDefinitionList.add(columnDefinition);

                String unWrapColumnName = columnDefinition.getUnWrapColumnName();

                this.columnNameToColumnDefinitionMap.put(unWrapColumnName, columnDefinition);
                this.modelFieldNameToColumnDefinitionMap.put(columnDefinition.getFieldName(), columnDefinition);
                this.modelFieldToColumnDefinitionMap.put(modelField, columnDefinition);

                if (columnDefinition.isPrimaryKey()) {
                    this.primaryKeyDefinitionList.add(columnDefinition);
                }
                if (columnDefinition.isUniqueKey()) {
                    this.uniqueKeyDefinitionList.add(columnDefinition);
                }
            }

            // 排序， 主键，唯一键， 其他
            Collections.sort(this.columnDefinitionList);
        }
    }

    /** java属性名称 --> 忽略的java属性 */
    private Map<String, Field> modelFieldNameToIgnoredModelFieldMap = new HashMap<String, Field>();

    /**
     * 忽略不必要的属性
     *
     * @param modelField 要检查的属性
     * @return
     */
    private boolean isIgnoreField(Field modelField) {
        if (!"serialVersionUID".equalsIgnoreCase(modelField.getName())) {
            return false;
        }

        IgnoreField ignoreFieldAnnotation = ReflectUtil.getFieldAnnotation(modelField, IgnoreField.class);

        if (null == ignoreFieldAnnotation) {
            return false;
        }

        modelFieldNameToIgnoredModelFieldMap.put(modelField.getName(), modelField);

        return true;
    }

    public boolean isIgnoredModelFieldName(String modelFieldName) {
        return modelFieldNameToIgnoredModelFieldMap.containsKey(modelFieldName);
    }

    public Class<?> getModelType() {
        return modelType;
    }

    public String getWrapTablename() {
        return wrapTablename;
    }

    public String getUnWrapTablename() {
        return unWrapTablename;
    }

    public Table getTableAnnotation() {
        return tableAnnotation;
    }

    public ColumnMapStrategy getTableColumnMapStrategy() {
        return tableColumnMapStrategy;
    }

    public ColumnDefinition getColumnDefinitionByColumnName(String columnName) {
        String unWrapColumnName = Util.unWrapColumnName(columnName);
        ColumnDefinition columnDefinition = this.columnNameToColumnDefinitionMap.get(unWrapColumnName);
        AssertUtil.assertNotNull(columnDefinition, this.getUnWrapTablename() + "." + unWrapColumnName + " 字段不存在！");
        return columnDefinition;
    }

    public ColumnDefinition getColumnDefinitionByModelFieldName(String modelFieldName) {
        ColumnDefinition columnDefinition = this.modelFieldNameToColumnDefinitionMap.get(modelFieldName);
        AssertUtil.assertNotNull(columnDefinition, this.modelType.getName() + "." + modelFieldName + " 属性不存在！");
        return columnDefinition;
    }

    public ColumnDefinition getColumnDefinitionByModelField(Field modelField) {
        ColumnDefinition columnDefinition = this.modelFieldToColumnDefinitionMap.get(modelField);
        AssertUtil.assertNotNull(columnDefinition, this.modelType.getName() + "." + modelField.getName() + " 属性不存在！");
        return columnDefinition;
    }

    /**
     * 根据数据库列的名称获取指定的Java字段属性
     *
     * @param columnName 数据库列名称
     * @return
     */
    public Field getModelFieldByColumnName(String columnName) {
        return getColumnDefinitionByColumnName(columnName).getField();
    }

    public String getModelFieldNameByColumnName(String columnName) {
        return getColumnDefinitionByColumnName(columnName).getFieldName();
    }

    public String getWrapColumnNameByModelField(Field modelField) {
        return getColumnDefinitionByModelField(modelField).getWrapColumnName();
    }

    public String getWrapColumnNameByModelFieldName(String modelFieldName) {
        return getColumnDefinitionByModelFieldName(modelFieldName).getWrapColumnName();
    }

    public List<ColumnDefinition> getColumnDefinitionList() {
        return columnDefinitionList;
    }

    public List<ColumnDefinition> getPrimaryKeyDefinitionList() {
        return primaryKeyDefinitionList;
    }

    public List<ColumnDefinition> getUniqueKeyDefinitionList() {
        return uniqueKeyDefinitionList;
    }

    public boolean containModelFieldName(String modelFieldName) {
        return modelFieldNameToColumnDefinitionMap.get(modelFieldName) != null;
    }

    public boolean containColumnName(String columnName) {
        return this.columnNameToColumnDefinitionMap.get(Util.unWrapColumnName(columnName)) != null;
    }
}
